#include "cfs_read_write.h"
#include "cfs_buffers.h"

// 获取extent地址
inline KPHCCfsExtentAddress *GetExtentAddress(KPHCCfsExtentHeader *header, uint16 blockOffset)
{
    auto chunkSize = header->chunk_size;
    auto headerOffset = offsetof(KPHCCfsExtentHeader, cfsExtentAddress);
    auto sizeOfExtentAddress = SizeOfExtentAddress(chunkSize);
    return (KPHCCfsExtentAddress *)(((char *)header) + headerOffset + blockOffset * sizeOfExtentAddress);
}

size_t KPHCCfsWritePage(SMgrRelation reln, ForkNumber forknum, BlockNumber logicBlockNumber, char *buffer, bool sipSync, CFS_STORAGE_TYPE type)
{
    KPHCExtentLocation location = cfsLocationConverts[type](reln, forknum, logicBlockNumber, sipSync, KPHC_EXTENT_OPEN_FILE);
    KPHCPciPageCtrl *ctrl = KPHCPciBufReadPage(location, LW_SHARED, PCI_BUF_NORMAL_READ);
    KPHCCfsExtentHeader *cfsExtentHeader = ctrl->pciPage;
    KPHCCfsExtentAddress *cfsExtentAddress = GetExtentAddress(cfsExtentAddress, (uint16)location.extentOffset);
    
    // 获取压缩参数
    KPHCRelFileCompressOption option;
    KPHCAnalyzeCompressOptions(reln->SMgrRelation.node, &option);
    uint16 chunkSize = cfsExtentHeader->chunk_size;

    // 执行页压缩
    uint8 nchunks;
    char *compressedBuffer = KPHCCfsCompressPage(buffer, &option, &nchunks);

    // 设置地址
    uint8 needChunks = option.compressPreallocChunks > nchunks ? (uint8)option.compressPreallocChunks : (uint8)nchunks;
    bool changed = ExtendChunksOfBlock(cfsExtentHeader, &location, needChunks, nchunks);

    off_t extentStartOffset = location.extentStart * BLCKSZ;

    // 写入每个chunk
    for (auto i = 0; i < nchunks; ++i) {
        auto bufferPos = compressedBuffer + (long)chunkSize * i;
        off_t seekPos = OffsetOfPageCompressChunk(chunkSize, cfsExtentAddress->chunknos[i]);
        if (cfsExtentAddress->chunknos[i] > ((BLCKSZ / chunkSize) * KPHC_CFS_LOGIC_BLOCKS_PER_EXTENT)) {
            *((uint32 *)nullptr) = 1; // ???
        }
        auto start = i;
        while (i < nchunks - 1 && cfsExtentAddress->chunknos[i + 1] == cfsExtentAddress->chunknos[i] + 1) {
            i++;
        }
        int writeAmount = chunkSize * ((i - start) + 1);
        if ((seekPos + extentStartOffset) > (((BlockNumber)RELSEG_SIZE) * BLCKSZ)) {
            *((uint32 *)nullptr) = 1; // ???
        }
        int nbytes = FilePWrite(location.fd, bufferPos, writeAmount, seekPos + extentStartOffset, (uint32)WAIT_EVENT_DATA_FILE_WRITE);
        if (nbytes != writeAmount) {
            if (compressedBuffer != nullptr && compressedBuffer != buffer) {
                pfree(compressedBuffer);
            }
            KPHCPciBufFreePage(ctrl, location, false);
        }
    }

    if (compressedBuffer != nullptr && compressedBuffer != buffer) {
        pfree(compressedBuffer);
    }
    KPHCPciBufFreePage(ctrl, location, false);
    return BLCKSZ;
}
